import axios from 'axios'
import type {
    AxiosInstance,
    AxiosResponse,
    Canceler,
    InternalAxiosRequestConfig,
    AxiosRequestConfig,
} from 'axios'
import { getToken, redirectLogin, removeToken, messageBox } from '@/utils/tools'
import config from '@/config'

// 请求参数 config 定义
export interface RequestConfig extends AxiosRequestConfig {
    isIntercept?: boolean // 是否推入全局的请求拦截栈, 主要做批量取消请求的操作
}

// 响应参数 定义
export type ResponseData<T, X> = X extends true
    ? T
    : {
          code: number
          data: null | T
          message: string
      }

let isWaitToLogin = false // 已经有异步跳转登录页面标识
const outLogin = () => {
    if (!config.isDev) {
        removeToken()
        isWaitToLogin = true
        setTimeout(() => {
            redirectLogin()
        }, 200)
    }
}

// 请求失败后的错误统一处理
const errorHandle = (status: number, message: string, type?: number) => {
    if (isWaitToLogin) return
    // 状态码判断
    switch (status) {
        // 400: 缺少认证的token，未授权的内部用户
        case 400:
            messageBox(`${message}`)
            // code400 才不跳转登录页 解决无限循环跳转的bug
            type === 1 && outLogin()
            break
        // 401: 未登录状态，跳转登录页
        case 401:
            messageBox(`${message}`)
            outLogin()
            break
        // 403 token过期 清除token并跳转登录页
        case 403:
            messageBox('登录过期，请重新登录即将跳转登录页...')
            outLogin()
            break
        // 服务器拥堵 Bad Gateway
        case 502:
            messageBox('网络拥堵...')
            break
        // 404请求不存在
        case 404:
            messageBox('请求的资源不存在', 'error')
            break
        // 500服务器端错误
        case 500:
            messageBox('服务器端错误', 'error')
            break
        default:
            break
    }
}

// 实例化请求配置
const instance: AxiosInstance = axios.create({
    baseURL: config.domain,
    timeout: config.requestTimeOut,
    validateStatus: () => true, // 使用async-await，处理reject情况较为繁琐，所以全部返回resolve，在业务代码中处理异常
})

// 请求拦截器
instance.interceptors.request.use(
    (params: InternalAxiosRequestConfig<any>) => {
        if (params && params.headers) {
            params.headers['Cache-Control'] = 'no-cache, no-store'
            params.headers.Pragma = 'no-cache'
            params.headers.token = getToken()
        }
        return params
    },
    error => {
        return Promise.reject(error)
    },
)

// 响应拦截器
instance.interceptors.response.use(
    // 请求成功
    (response: AxiosResponse<any>) => {
        if (response.status === 200) {
            const { code, message } = response.data
            // 1、存在code, 且是获取数据成功的code
            if (code === 200 || code === 'success') {
                return response.data
            }
            // 2、存在code, 但是上一步骤没有通过，可以认为是获取数据失败的code
            if (typeof code === 'number' || typeof code === 'string') {
                messageBox(message)

                if (code === 400) {
                    errorHandle(code, response.data.message, 1)
                }

                return Promise.reject(response.data)
            }
            // 3、不存在code的数据，无法确认是获取成功， 还是失败，由业务代码自行判断
            return response.data
        } else {
            errorHandle(response.status, response.data.message)
        }
    },
    // 请求失败
    error => {
        if (axios.isCancel(error)) {
            console.warn(error.message)
        }
        const { response } = error
        if (response) {
            // 请求已发出，但是状态不在2xx的范围
            errorHandle(response.status, response.data.message)
            return Promise.reject(response)
        }
    },
)

const CancelToken = axios.CancelToken
const interceptList: Canceler[] = []

// R为false时使用标准的返回体数据格式 R为true表示不使用任务封装返回体数据格式
const request = <T, R = false>(option: RequestConfig) => {
    if (option.isIntercept) {
        option.cancelToken = new CancelToken(cancel => {
            interceptList.push(cancel)
        })
    }
    return instance.request<any, ResponseData<T, R>>(option)
}
export default request
